﻿// CMakeProject.cpp: 定义应用程序的入口点。
//

#include "receiver.h"

struct multicast_receiver::multicast_receiver_impl
{
    boost::asio::ip::udp::socket socket_;
    boost::asio::ip::udp::endpoint sender_endpoint_;
    std::function<void(boost::system::error_code& ec
        , const char* data, std::size_t length
        , const boost::asio::ip::udp::endpoint& sender_endpoint)> receiver_;
    std::vector<char> data_;
    multicast_receiver_impl(boost::asio::io_context& io_context,
        std::function<void(boost::system::error_code& ec
        , const char* data, std::size_t length
        , const boost::asio::ip::udp::endpoint& sender_endpoint)> receiver
        , size_t buffer_max)
        : socket_(io_context)
        , receiver_(receiver)
    {
        data_.resize(buffer_max);
    }
};

multicast_receiver::multicast_receiver(boost::asio::io_context& io_context
    , const boost::asio::ip::address& listen_address
    , const unsigned short& multicast_port
    , const boost::asio::ip::address& multicast_address
    , std::function<void(boost::system::error_code& ec
        , const char* data, std::size_t length
        , const boost::asio::ip::udp::endpoint& sender_endpoint)> receiver
    , size_t buffer_max
)
{
    impl = new multicast_receiver_impl(io_context, receiver, buffer_max);
    // Create the socket so that multiple may be bound to the same address.
    boost::asio::ip::udp::endpoint listen_endpoint(
        listen_address, multicast_port);
    impl->socket_.open(listen_endpoint.protocol());
    impl->socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
    impl->socket_.bind(listen_endpoint);

    // Join the multicast group.
    impl->socket_.set_option(
        boost::asio::ip::multicast::join_group(multicast_address));

    do_receive();
}

multicast_receiver::~multicast_receiver()
{
    impl->socket_.cancel();
    delete impl;
}


void multicast_receiver::do_receive()
{
    impl->socket_.async_receive_from(
        boost::asio::buffer(impl->data_), impl->sender_endpoint_,
        [this](boost::system::error_code ec, std::size_t length)
        {
            if (impl->receiver_)
            {
                (impl->receiver_)(ec, impl->data_.data(), length, impl->sender_endpoint_);
            }
            if (!ec)
            {
                do_receive();
            }
        });
}


